package ph.com.gs3.formalistics.presenter.fragments.workers;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import ph.com.gs3.formalistics.model.communicators.DocumentCommunicatorModule;
import ph.com.gs3.formalistics.model.communicators.HttpCommunicator;
import ph.com.gs3.formalistics.model.dao.DocumentsDAO;
import ph.com.gs3.formalistics.model.dao.FormsDAO;
import ph.com.gs3.formalistics.model.dao.OutgoingDocumentsDAO;
import ph.com.gs3.formalistics.model.dao.OutgoingStarMarksDAO;
import ph.com.gs3.formalistics.model.valueobjects.business.document.Document;
import ph.com.gs3.formalistics.model.valueobjects.business.document.OutgoingDocument;
import ph.com.gs3.formalistics.model.valueobjects.business.document.Document.DocumentStar;
import ph.com.gs3.formalistics.model.valueobjects.business.form.Form;
import ph.com.gs3.formalistics.model.valueobjects.business.form.FormField.InvalidFormFieldException;
import ph.com.gs3.formalistics.presenter.DocumentActivity.DocumentType;
import ph.com.gs3.formalistics.service.managers.DocumentManager;
import ph.com.gs3.formalistics.service.managers.DocumentManager.DocumentSaveFailedException;
import android.app.Fragment;
import android.content.Context;

public class DocumentWorkerFragment extends Fragment {

	public static final String TAG = DocumentWorkerFragment.class.getSimpleName();

	// data fields
	private Form currentForm;
	private Document currentDocument;
	private OutgoingDocument currentOutgoingDocument;

	private String server;

	// dependencies
	private FormsDAO formsDAO;
	private DocumentsDAO documentsDAO;
	private OutgoingDocumentsDAO outgoingDocumentsDAO;
	private OutgoingStarMarksDAO outgoingStarMarksDAO;

	private DocumentManager documentManager;

	public static DocumentWorkerFragment createInstance(Context applicationContext, String server) {

		DocumentWorkerFragment instance = new DocumentWorkerFragment();
		instance.server = server;
		instance.initializeDependencies(applicationContext);

		return instance;

	}

	private void initializeDependencies(Context context) {
		formsDAO = new FormsDAO(context);
		documentsDAO = new DocumentsDAO(context);
		outgoingDocumentsDAO = new OutgoingDocumentsDAO(context);
		outgoingStarMarksDAO = new OutgoingStarMarksDAO(context);

		DocumentCommunicatorModule documentCommModule = new DocumentCommunicatorModule(
		        new HttpCommunicator(), server);
		documentManager = new DocumentManager(formsDAO, documentsDAO, outgoingDocumentsDAO,
		        outgoingStarMarksDAO, documentCommModule);

	}

	public void initialize(DocumentType documentType, String formDbId, String documentDbId) {

		if (documentType == DocumentType.NEW_DOCUMENT) {

			try {
				currentForm = getForm(formDbId);

				// create new blank document
				currentDocument = Document.createNewBlankDocument(server, currentForm.getWebId());

			} catch (JSONException | InvalidFormFieldException e) {
				e.printStackTrace();
				// TODO: notify the user that the form cannot be opened
			}
		} else {

			String formWebId = null;
			String server = null;

			if (documentType == DocumentType.EXISTING_DOCUMENT) {
				try {
					currentDocument = getDocument(documentDbId);
					formWebId = currentDocument.getFormWebId();
					server = currentDocument.getServer();
				} catch (JSONException e) {
					e.printStackTrace();
					// TODO: notify the user that the document cannot be opened
				}
			} else {
				currentOutgoingDocument = getOutgoingDocument(documentDbId);
				formWebId = currentOutgoingDocument.getDocumentFormWebId();
				server = currentOutgoingDocument.getServer();
			}

			try {
				currentForm = getForm(formWebId, server);
			} catch (JSONException | InvalidFormFieldException e) {
				e.printStackTrace();
				// TODO: notify the user that the form cannot be opened/is corrupted
			}
		}

	}

	public List<String> validateFields(JSONObject updatedDocumentFields, DocumentType documentType) {

		List<String> fieldsThatFailedValidation = new ArrayList<>();
		List<String> fieldsRequired = new ArrayList<>();

		if (documentType == DocumentType.NEW_DOCUMENT) {
			fieldsRequired = currentForm.getOnCreateFieldsRequired();
		} else if (documentType == DocumentType.EXISTING_DOCUMENT) {
			fieldsRequired = currentDocument.getFieldsRequired();
		}

		for (String requiredField : fieldsRequired) {
			boolean passed = false;
			if (updatedDocumentFields.has(requiredField)) {
				String fieldValue;
				try {
					fieldValue = updatedDocumentFields.getString(requiredField);
					if (fieldValue != null && !fieldValue.trim().isEmpty()) {
						passed = true;
					}
				} catch (JSONException e) {
					// failed, print exception
					e.printStackTrace();
				}
			}

			if (!passed) {
				fieldsThatFailedValidation.add(requiredField);
			}
		}

		return fieldsThatFailedValidation;

	}

	public void saveDocumentAction(JSONObject newFieldValues, String action, String currentUserDbId) {

		// update the document before adding it
		currentDocument.setFields(newFieldValues);
		try {
			documentManager.addDocumentToOutgoingDocuments(currentDocument, currentForm.getName(),
			        action, currentUserDbId);
		} catch (DocumentSaveFailedException e) {
			e.printStackTrace();
		}

	}

	public void markDocumentStar(String userDbId, DocumentStar starMark) {

		String formWebId = currentForm.getWebId();
		String documentWebId = currentDocument.getWebId();
		String documentDbId = currentDocument.getDbId();

		documentManager.saveDocumentStarAsOutgoing(formWebId, documentWebId, documentDbId,
		        userDbId, starMark);

	}

	// =================================================================
	// {{ Getters & Setters

	public Form getCurrentForm() {
		return currentForm;
	}

	public Document getCurrentDocument() {
		return currentDocument;
	}

	public OutgoingDocument getCurrentOutgoingDocument() {
		return currentOutgoingDocument;
	}

	private Form getForm(String dbId) throws JSONException, InvalidFormFieldException {

		Form form = null;

		formsDAO.open();
		try {
			form = formsDAO.getForm(dbId);
		} finally {
			formsDAO.close();
		}

		return form;
	}

	private Form getForm(String formWebId, String server) throws JSONException,
	        InvalidFormFieldException {

		Form form = null;

		formsDAO.open();
		try {
			form = formsDAO.getForm(formWebId, server);
		} finally {
			formsDAO.close();
		}

		return form;

	}

	private Document getDocument(String documentDbId) throws JSONException {

		Document document = null;

		documentsDAO.open();
		try {
			document = documentsDAO.getDocument(documentDbId);
		} finally {
			documentsDAO.close();
		}

		return document;

	}

	private OutgoingDocument getOutgoingDocument(String outgoingDocumentDbId) {

		OutgoingDocument document = null;

		outgoingDocumentsDAO.open();
		try {
			document = outgoingDocumentsDAO.getOutgoingDocument(outgoingDocumentDbId);
		} finally {
			outgoingDocumentsDAO.close();
		}

		return document;

	}

	// }}

	// =================================================================
	// {{ Interfaces

	public static interface DocumentWorkerActionListener {

		public void onCurrentDocumentStarred();

		public void onCurrentDocumentUnstarred();

	}

	// }}

}
